#define _POSIX_C_SOURCE 200112L
#define _DARWIN_C_SOURCE
#include "file_system.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <errno.h>
#include <time.h>
#include <inttypes.h>

// CRC32 implementation for checksums
static const uint32_t crc32_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

static uint32_t calculate_crc32(const void* data, size_t len) {
    const uint8_t* bytes = (const uint8_t*)data;
    uint32_t crc = 0xFFFFFFFF;
    
    for (size_t i = 0; i < len; i++) {
        crc = crc32_table[(crc ^ bytes[i]) & 0xFF] ^ (crc >> 8);
    }
    
    return crc ^ 0xFFFFFFFF;
}

static uint64_t get_current_time_ms(void) {
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

// File system initialization
file_system* fs_init(const char* data_dir) {
    if (!data_dir) {
        return NULL;
    }
    
    file_system* fs = calloc(1, sizeof(file_system));
    if (!fs) {
        return NULL;
    }
    
    // Initialize LSN manager
    fs->lsn_mgr.current_lsn = 1;
    fs->lsn_mgr.checkpoint_lsn = 0;
    fs->lsn_mgr.flush_lsn = 0;
    
    // Initialize page buffers (simple implementation)
    fs->num_page_buffers = GUDB_PAGE_CACHE_SIZE / GUDB_PAGE_SIZE;
    fs->page_buffers = calloc(fs->num_page_buffers, sizeof(page_buffer));
    if (!fs->page_buffers) {
        free(fs);
        return NULL;
    }
    
    // Allocate memory for page buffers
    for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
        fs->page_buffers[i].page = malloc(GUDB_PAGE_SIZE);
        if (!fs->page_buffers[i].page) {
            // Cleanup on failure
            for (uint32_t j = 0; j < i; j++) {
                free(fs->page_buffers[j].page);
            }
            free(fs->page_buffers);
            free(fs);
            return NULL;
        }
        fs->page_buffers[i].page_id = UINT32_MAX; // Invalid page ID
        fs->page_buffers[i].valid = false;
    }
    
    // Open all required files
    char filepath[512];
    
    // Data file
    snprintf(filepath, sizeof(filepath), "%s/gudb.dat", data_dir);
    if (fs_open_file(&fs->data_file, filepath, O_RDWR | O_CREAT) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // WAL file
    snprintf(filepath, sizeof(filepath), "%s/gudb.wal", data_dir);
    if (fs_open_file(&fs->wal_file, filepath, O_RDWR | O_CREAT) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // Binlog file
    snprintf(filepath, sizeof(filepath), "%s/gudb.binlog", data_dir);
    if (fs_open_file(&fs->binlog_file, filepath, O_RDWR | O_CREAT) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // Meta file
    snprintf(filepath, sizeof(filepath), "%s/gudb.meta", data_dir);
    if (fs_open_file(&fs->meta_file, filepath, O_RDWR | O_CREAT) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // Log file
    snprintf(filepath, sizeof(filepath), "%s/gudb.log", data_dir);
    if (fs_open_file(&fs->log_file, filepath, O_WRONLY | O_CREAT | O_APPEND) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // Lock file
    snprintf(filepath, sizeof(filepath), "%s/gudb.lock", data_dir);
    if (fs_open_file(&fs->lock_file, filepath, O_RDWR | O_CREAT) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    // Lock the database
    if (fs_lock_database(fs) != PAGE_OK) {
        fs_cleanup(fs);
        return NULL;
    }
    
    return fs;
}

// File system cleanup
int fs_cleanup(file_system* fs) {
    if (!fs) {
        return PAGE_OK;
    }
    
    // Unlock database
    fs_unlock_database(fs);
    
    // Close all files
    fs_close_file(&fs->data_file);
    fs_close_file(&fs->wal_file);
    fs_close_file(&fs->binlog_file);
    fs_close_file(&fs->meta_file);
    fs_close_file(&fs->log_file);
    fs_close_file(&fs->lock_file);
    
    // Free page buffers
    if (fs->page_buffers) {
        for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
            if (fs->page_buffers[i].page) {
                free(fs->page_buffers[i].page);
            }
        }
        free(fs->page_buffers);
    }
    
    free(fs);
    return PAGE_OK;
}

// File operations
int fs_open_file(file_handle* handle, const char* filepath, int flags) {
    if (!handle || !filepath) {
        return PAGE_ERR_IO;
    }
    
    strncpy(handle->filepath, filepath, sizeof(handle->filepath) - 1);
    handle->filepath[sizeof(handle->filepath) - 1] = '\0';
    
    handle->fd = open(filepath, flags, 0644);
    if (handle->fd < 0) {
        return PAGE_ERR_IO;
    }
    
    // Get file size
    struct stat st;
    if (fstat(handle->fd, &st) == 0) {
        handle->size = st.st_size;
    } else {
        handle->size = 0;
    }
    
    handle->is_mmaped = false;
    handle->mmap_addr = NULL;
    handle->mmap_size = 0;
    
    return PAGE_OK;
}

int fs_close_file(file_handle* handle) {
    if (!handle) {
        return PAGE_OK;
    }
    
    // Unmap if mapped
    if (handle->is_mmaped) {
        fs_munmap_file(handle);
    }
    
    // Close file descriptor
    if (handle->fd >= 0) {
        close(handle->fd);
        handle->fd = -1;
    }
    
    return PAGE_OK;
}

int fs_sync_file(file_handle* handle) {
    if (!handle || handle->fd < 0) {
        return PAGE_ERR_IO;
    }
    
    if (fsync(handle->fd) != 0) {
        return PAGE_ERR_IO;
    }
    
    return PAGE_OK;
}

// Page operations
int fs_read_page(file_system* fs, uint32_t page_id, gudb_page* page) {
    if (!fs || !page) {
        return PAGE_ERR_IO;
    }
    
    // Check page cache first
    for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
        if (fs->page_buffers[i].valid && fs->page_buffers[i].page_id == page_id) {
            memcpy(page, fs->page_buffers[i].page, GUDB_PAGE_SIZE);
            fs->page_buffers[i].last_access = get_current_time_ms();
            fs->page_cache_hits++;
            return PAGE_OK;
        }
    }
    
    fs->page_cache_misses++;
    
    // Read from disk
    off_t offset = (off_t)page_id * GUDB_PAGE_SIZE;
    if (lseek(fs->data_file.fd, offset, SEEK_SET) != offset) {
        return PAGE_ERR_IO;
    }
    
    ssize_t bytes_read = read(fs->data_file.fd, page, GUDB_PAGE_SIZE);
    if (bytes_read != GUDB_PAGE_SIZE) {
        // If we read less than a full page, it might be a new page
        if (bytes_read >= 0) {
            memset((char*)page + bytes_read, 0, GUDB_PAGE_SIZE - bytes_read);
        } else {
            return PAGE_ERR_IO;
        }
    }
    
    // Validate page
    if (!fs_validate_page(page)) {
        return PAGE_ERR_CORRUPT;
    }
    
    // Cache the page (simple LRU replacement)
    uint32_t lru_index = 0;
    uint64_t oldest_access = UINT64_MAX;
    
    for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
        if (!fs->page_buffers[i].valid) {
            lru_index = i;
            break;
        }
        if (fs->page_buffers[i].last_access < oldest_access) {
            oldest_access = fs->page_buffers[i].last_access;
            lru_index = i;
        }
    }
    
    // Write back dirty page if needed
    if (fs->page_buffers[lru_index].valid && fs->page_buffers[lru_index].dirty) {
        fs_write_page(fs, fs->page_buffers[lru_index].page_id, fs->page_buffers[lru_index].page);
    }
    
    // Cache the new page
    memcpy(fs->page_buffers[lru_index].page, page, GUDB_PAGE_SIZE);
    fs->page_buffers[lru_index].page_id = page_id;
    fs->page_buffers[lru_index].valid = true;
    fs->page_buffers[lru_index].dirty = false;
    fs->page_buffers[lru_index].last_access = get_current_time_ms();
    
    fs->pages_read++;
    return PAGE_OK;
}

int fs_write_page(file_system* fs, uint32_t page_id, const gudb_page* page) {
    if (!fs || !page) {
        return PAGE_ERR_IO;
    }
    
    // Update page in cache if present
    for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
        if (fs->page_buffers[i].valid && fs->page_buffers[i].page_id == page_id) {
            memcpy(fs->page_buffers[i].page, page, GUDB_PAGE_SIZE);
            fs->page_buffers[i].dirty = true;
            fs->page_buffers[i].last_access = get_current_time_ms();
            break;
        }
    }
    
    // Write to disk
    off_t offset = (off_t)page_id * GUDB_PAGE_SIZE;
    if (lseek(fs->data_file.fd, offset, SEEK_SET) != offset) {
        return PAGE_ERR_IO;
    }
    
    ssize_t bytes_written = write(fs->data_file.fd, page, GUDB_PAGE_SIZE);
    if (bytes_written != GUDB_PAGE_SIZE) {
        return PAGE_ERR_IO;
    }
    
    fs->pages_written++;
    return PAGE_OK;
}

int fs_allocate_page(file_system* fs, uint32_t* page_id) {
    if (!fs || !page_id) {
        return PAGE_ERR_IO;
    }
    
    // Simple allocation: use file size / page size as next page ID
    *page_id = (uint32_t)(fs->data_file.size / GUDB_PAGE_SIZE);
    
    // Initialize new page
    gudb_page new_page;
    memset(&new_page, 0, GUDB_PAGE_SIZE);
    new_page.lsn = fs_get_next_lsn(fs);
    new_page.flags = 0;
    new_page.free_space = GUDB_PAGE_SIZE - 16; // Header is 16 bytes
    new_page.checksum = fs_calculate_checksum(&new_page);
    
    // Write the new page
    if (fs_write_page(fs, *page_id, &new_page) != PAGE_OK) {
        return PAGE_ERR_IO;
    }
    
    // Update file size
    fs->data_file.size += GUDB_PAGE_SIZE;
    
    return PAGE_OK;
}

int fs_free_page(file_system* fs, uint32_t page_id) {
    if (!fs) {
        return PAGE_ERR_IO;
    }
    
    // Mark page as free in cache
    for (uint32_t i = 0; i < fs->num_page_buffers; i++) {
        if (fs->page_buffers[i].valid && fs->page_buffers[i].page_id == page_id) {
            fs->page_buffers[i].valid = false;
            fs->page_buffers[i].dirty = false;
            break;
        }
    }
    
    // In a real implementation, we would maintain a free page list
    // For now, just zero out the page
    gudb_page empty_page;
    memset(&empty_page, 0, GUDB_PAGE_SIZE);
    
    return fs_write_page(fs, page_id, &empty_page);
}

// Page validation and checksums
uint32_t fs_calculate_checksum(const gudb_page* page) {
    if (!page) {
        return 0;
    }
    
    // Calculate checksum of everything except the checksum field itself
    const char* data = (const char*)page;
    size_t checksum_offset = offsetof(gudb_page, checksum);
    
    // Hash data before checksum field
    uint32_t crc = calculate_crc32(data, checksum_offset);
    
    // Hash data after checksum field
    const char* after_checksum = data + checksum_offset + sizeof(uint32_t);
    size_t remaining_len = GUDB_PAGE_SIZE - checksum_offset - sizeof(uint32_t);
    
    crc = calculate_crc32(after_checksum, remaining_len) ^ crc;
    
    return crc;
}

bool fs_validate_page(const gudb_page* page) {
    if (!page) {
        return false;
    }
    
    uint32_t calculated_checksum = fs_calculate_checksum(page);
    return calculated_checksum == page->checksum;
}

int fs_repair_page(gudb_page* page) {
    if (!page) {
        return PAGE_ERR_IO;
    }
    
    // Recalculate and fix checksum
    page->checksum = fs_calculate_checksum(page);
    
    // Basic sanity checks
    if (page->free_space > GUDB_PAGE_SIZE - 16) {
        page->free_space = GUDB_PAGE_SIZE - 16;
    }
    
    return PAGE_OK;
}

// LSN management
uint64_t fs_get_next_lsn(file_system* fs) {
    if (!fs) {
        return 0;
    }
    
    return __sync_fetch_and_add(&fs->lsn_mgr.current_lsn, 1);
}

int fs_set_page_lsn(gudb_page* page, uint64_t lsn) {
    if (!page) {
        return PAGE_ERR_IO;
    }
    
    page->lsn = lsn;
    page->checksum = fs_calculate_checksum(page);
    
    return PAGE_OK;
}

uint64_t fs_get_page_lsn(const gudb_page* page) {
    if (!page) {
        return 0;
    }
    
    return page->lsn;
}

// File locking
int fs_lock_database(file_system* fs) {
    if (!fs || fs->lock_file.fd < 0) {
        return PAGE_ERR_IO;
    }
    
    if (flock(fs->lock_file.fd, LOCK_EX | LOCK_NB) != 0) {
        return PAGE_ERR_IO;
    }
    
    return PAGE_OK;
}

int fs_unlock_database(file_system* fs) {
    if (!fs || fs->lock_file.fd < 0) {
        return PAGE_OK;
    }
    
    flock(fs->lock_file.fd, LOCK_UN);
    return PAGE_OK;
}

// Memory mapping
int fs_mmap_file(file_handle* handle, size_t size) {
    if (!handle || handle->fd < 0) {
        return PAGE_ERR_IO;
    }
    
    if (handle->is_mmaped) {
        fs_munmap_file(handle);
    }
    
    handle->mmap_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);
    if (handle->mmap_addr == MAP_FAILED) {
        handle->mmap_addr = NULL;
        return PAGE_ERR_IO;
    }
    
    handle->mmap_size = size;
    handle->is_mmaped = true;
    
    return PAGE_OK;
}

int fs_munmap_file(file_handle* handle) {
    if (!handle || !handle->is_mmaped) {
        return PAGE_OK;
    }
    
    if (munmap(handle->mmap_addr, handle->mmap_size) != 0) {
        return PAGE_ERR_IO;
    }
    
    handle->mmap_addr = NULL;
    handle->mmap_size = 0;
    handle->is_mmaped = false;
    
    return PAGE_OK;
}

// Utility functions
uint64_t fs_get_file_size(const file_handle* handle) {
    if (!handle) {
        return 0;
    }
    
    return handle->size;
}

bool fs_file_exists(const char* filepath) {
    if (!filepath) {
        return false;
    }
    
    return access(filepath, F_OK) == 0;
}

int fs_create_directory(const char* dir_path) {
    if (!dir_path) {
        return PAGE_ERR_IO;
    }
    
    if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) {
        return PAGE_ERR_IO;
    }
    
    return PAGE_OK;
}

// Debug and statistics
void fs_print_stats(const file_system* fs) {
    if (!fs) {
        return;
    }
    
    printf("File System Statistics:\n");
    printf("  Pages read: %" PRIu64 "\n", fs->pages_read);
    printf("  Pages written: %" PRIu64 "\n", fs->pages_written);
    printf("  Cache hits: %" PRIu64 "\n", fs->page_cache_hits);
    printf("  Cache misses: %" PRIu64 "\n", fs->page_cache_misses);
    printf("  Cache hit ratio: %.2f%%\n", 
           fs->page_cache_hits + fs->page_cache_misses > 0 ?
           (double)fs->page_cache_hits / (fs->page_cache_hits + fs->page_cache_misses) * 100.0 : 0.0);
    printf("  Current LSN: %" PRIu64 "\n", fs->lsn_mgr.current_lsn);
    printf("  Checkpoint LSN: %" PRIu64 "\n", fs->lsn_mgr.checkpoint_lsn);
    printf("  Flush LSN: %" PRIu64 "\n", fs->lsn_mgr.flush_lsn);
}

int fs_verify_integrity(file_system* fs) {
    if (!fs) {
        return PAGE_ERR_IO;
    }
    
    // Basic integrity checks
    int errors = 0;
    
    // Check if all files are accessible
    if (fs->data_file.fd < 0) errors++;
    if (fs->wal_file.fd < 0) errors++;
    if (fs->binlog_file.fd < 0) errors++;
    if (fs->meta_file.fd < 0) errors++;
    if (fs->log_file.fd < 0) errors++;
    if (fs->lock_file.fd < 0) errors++;
    
    return errors == 0 ? PAGE_OK : PAGE_ERR_CORRUPT;
}

// Accessor functions for file descriptors
int fs_get_data_fd(const file_system* fs) {
    return fs ? fs->data_file.fd : -1;
}

int fs_get_wal_fd(const file_system* fs) {
    return fs ? fs->wal_file.fd : -1;
}

int fs_get_binlog_fd(const file_system* fs) {
    return fs ? fs->binlog_file.fd : -1;
}

int fs_get_meta_fd(const file_system* fs) {
    return fs ? fs->meta_file.fd : -1;
}

int fs_get_log_fd(const file_system* fs) {
    return fs ? fs->log_file.fd : -1;
}

int fs_get_lock_fd(const file_system* fs) {
    return fs ? fs->lock_file.fd : -1;
}